/*
 * Copyright (c) 2004-2006, Volatile-Engine All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Volatile-Engine nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
/**
 * 
 */
package com.volatileengine.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import com.volatileengine.resources.IResourceFinder;
import com.volatileengine.xml.elements.Element;

/**
 * @author Administrator
 * 
 */
public class XMLLoader<T> implements IFileLoader<T> {

	private XmlPullParserFactory factory;
	private IResourceFinder resourceFinder;
	private Stack<Element<?>> tagStack;
	private HashMap<String, String> variables;
	private int holderForStartAndLength[] = new int[2];
	private Element<?> lastElement;

	public XMLLoader(Element<T> startElement) {
		try {
			factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
		} catch (XmlPullParserException e) {
			e.printStackTrace();
		}
		tagStack = new Stack<Element<?>>();
		variables = new HashMap<String, String>();

		lastElement = startElement;
	}

	public IResourceFinder getResourceFinder() {
		return resourceFinder;
	}

	public void setResourceFinder(IResourceFinder finder) {
		resourceFinder = finder;
	}

	public String getVariable(String name) {
		return variables.get(name);
	}

	public void setVariable(String name, String value) {
		variables.put(name, value);
	}

	public Map<String, String> getVariableMap() {
		return variables;
	}

	@SuppressWarnings("unchecked")
	public T load(String name, Object parameter) throws IOException {
		if (resourceFinder == null) {
			throw new XMLParsingException("ResourceFinder is null. Please call setResourceFinder(IResourceFinder)");
		}
		XmlPullParser xpp = null;
		try {
			xpp = factory.newPullParser();
			InputStream stream = resourceFinder.findResource(name);
			if (stream == null) {
				throw new IOException("Could not find " + name + " using supplied IResourceFinder");
			}
			xpp.setInput(stream, null);

			int eventType = xpp.getEventType();
			do {
				if (eventType == XmlPullParser.START_TAG) {
					processStartTag(xpp);
				} else if (eventType == XmlPullParser.TEXT || eventType == XmlPullParser.CDSECT) {
					processText(xpp);
				} else if (eventType == XmlPullParser.END_TAG) {
					processEndTag(xpp);
				}

				eventType = xpp.nextToken();
			} while (eventType != XmlPullParser.END_DOCUMENT);

			// we know to cast T because the lastElement is the first pushed
			// after the loops have finished
			return (T) lastElement.getValue();
		} catch (Exception ex) {
			throw new XMLParsingException("XML Parsing Exception on " + xpp.getLineNumber(), ex);
		}
	}

	private void processStartTag(XmlPullParser xpp) {
		HashMap<String, String> attribs = null;
		if (xpp.getAttributeCount() > 0) {
			attribs = new HashMap<String, String>();
		}
		for (int i = 0; i < xpp.getAttributeCount(); i++) {
			attribs.put(xpp.getAttributeName(i), xpp.getAttributeValue(i));
		}

		if (tagStack.size() == 0) {
			tagStack.push(lastElement);
			lastElement.startElement(attribs, this);
		} else {
			Element<?> element = tagStack.peek();
			Element<?> child = element.instantiateChild(xpp.getName(), this);
			child.startElement(attribs, this);
			tagStack.push(child);
		}
	}

	private void processEndTag(XmlPullParser xpp) {
		Element<?> element = tagStack.pop();
		element.endElement(this);
		lastElement = element;

		if (tagStack.size() > 0) {
			tagStack.peek().processChild(element, this);
		}
	}

	private void processText(XmlPullParser xpp) throws XmlPullParserException {
		char ch[] = xpp.getTextCharacters(holderForStartAndLength);
		int start = holderForStartAndLength[0];
		int length = holderForStartAndLength[1];
		String text = new String(ch, start, length);

		Element<?> element = tagStack.peek();
		element.append(text.getBytes());

	}
}
